package de.idgard.android.addon.proxy.services.impl;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.apache.http.HttpHost;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import de.idgard.android.addon.proxy.services.ProxyService;
import de.idgard.android.addon.ui.services.TransparencyStateService;
import de.idgard.android.addon.ui.services.TransparencyStateService.TransparencyState;
import de.idgard.android.addon.ui.services.dto.BaseState;
import de.idgard.android.proxy.IDGardProxyServer;
import de.idgard.android.service.IDGardProxyService;
import de.idgard.android.utils.ReflectionUtils;

/**
 * 
 * @author ckaratza
 * 
 */

class ProxyServiceImpl implements ProxyService {

	private BaseState state = BaseState.OFF;
	private Context context;
	private int port;

	private IDGardProxyServer communicator = null;

	public IDGardProxyServer getCommunicator() {
		return communicator;
	}

	public void setCommunicator(IDGardProxyServer communicator) {
		this.communicator = communicator;
	}

	public int getRunningPort() {
		return port;
	}

	private int lastRunningPort = 0;

	public int getLastRunningPort() {
		return lastRunningPort;
	}

	public void setContext(Context context) {
		this.context = context;
	}

	@Override
	public boolean startProxyService() {
		try {
			ComponentName cname = context.startService(new Intent(context, IDGardProxyService.class));
			if (cname != null) {
				return true;
			}
			return false;

		} catch (Throwable t) {
			return false;
		}
	}

	@Override
	public boolean startLocalProxy(String localhost, int boundLocalPort, String userName, String pwd) {
		try {
			boolean proxySet = setLocalProxy(context, localhost, boundLocalPort, userName, pwd);
			if (proxySet) {
				port = boundLocalPort;
				lastRunningPort = boundLocalPort;
				state = BaseState.ON;
				return true;
			}
			return false;

		} catch (Throwable t) {
			return false;
		}
	}

	@Override
	public boolean stopServiceAndLocalProxy() {
		try {
			resetProxy();
			boolean stopped = context.stopService(new Intent(context, IDGardProxyService.class));
			if (stopped) {
				port = 0;
				state = BaseState.OFF;
				return true;
			}
			return false;
		} catch (Throwable e) {
			return false;
		}
	}

	@Override
	public boolean isLocalProxyON() {
		return BaseState.ON.equals(state);
	}

	private final int PROXY_CHANGED = 193;

	private boolean setICSProxy(String host, int port) throws ClassNotFoundException, NoSuchMethodException, IllegalArgumentException, InstantiationException,
			IllegalAccessException, InvocationTargetException {
		Class webViewCoreClass = Class.forName("android.webkit.WebViewCore");
		Class proxyPropertiesClass = Class.forName("android.net.ProxyProperties");

		if (webViewCoreClass != null && proxyPropertiesClass != null) {
			Method m = webViewCoreClass.getDeclaredMethod("sendStaticMessage", Integer.TYPE, Object.class);
			Constructor c = proxyPropertiesClass.getConstructor(String.class, Integer.TYPE, String.class);
			m.setAccessible(true);
			c.setAccessible(true);
			Object properties = c.newInstance(host, port, null);
			m.invoke(null, PROXY_CHANGED, properties);
			return true;
		}
		return false;

	}

	private boolean setLocalProxy(Context ctx, String host, int port, String userName, String pwd) {
		boolean ret = false;
		try {
			if (Build.VERSION.SDK_INT < 14) {
				try {
					ret = setICSProxy(host, port);
				} catch (Throwable re) {
					Object requestQueueObject = getRequestQueue(ctx);
					if (requestQueueObject != null) {
						HttpHost httpHost = host == null ? null : new HttpHost(host, port, "http");
						ReflectionUtils.setDeclaredField(requestQueueObject, "mProxyHost", httpHost);
						ret = true;
						setProxyAuthenticationData(ctx, userName, pwd);
					}
				}
			} else {
				ret = setICSProxy(host, port);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		return ret;
	}

	private void resetProxy() throws Exception {
		setLocalProxy(context, null, 0, null, null);
	}

	private Object GetNetworkInstance(Context ctx) throws ClassNotFoundException {
		Class networkClass = Class.forName("android.webkit.Network");
		return networkClass;
	}

	private void setProxyAuthenticationData(Context ctx, final String userName, final String pwd) throws Exception {
		Object networkClass = GetNetworkInstance(ctx);
		Object networkObj = ReflectionUtils.invokeMethod(networkClass, "getInstance", new Object[] { ctx }, Context.class);

		if (networkObj != null) {
			ReflectionUtils.invokeMethod(networkObj, "setProxyUsername", new Object[] { userName }, new Class[] { String.class });
			ReflectionUtils.invokeMethod(networkObj, "setProxyPassword", new Object[] { pwd }, new Class[] { String.class });
		}
	}

	private Object getRequestQueue(Context ctx) throws Exception {
		Object ret = null;
		Object networkClass = GetNetworkInstance(ctx);
		if (networkClass != null) {

			Object networkObj = ReflectionUtils.invokeMethod(networkClass, "getInstance", new Object[] { ctx }, Context.class);

			if (networkObj != null) {
				ret = ReflectionUtils.getDeclaredField(networkObj, "mRequestQueue");
			}
		}
		return ret;
	}

	@Override
	public boolean adjustProxySettingsBasedOnTransparency(TransparencyState state) {
		if (TransparencyStateService.TransparencyState.TRANSPARENT == state) {
			return stopServiceAndLocalProxy();
		} else {
			if (this.state == BaseState.OFF) {
				return startProxyService();
			}
		}
		return false;
	}

	@Override
	public void setCommunicatorsAuthenticationString(String authString) {
		if (communicator == null) {
			return;
		}
		communicator.setUserAgentString(authString);
	}

	@Override
	public String getCommunicatorsAuthenticationString() {
		if (communicator == null) {
			return null;
		}
		return communicator.getUserAgentString();
	}
}
